home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mint110s / rendez.c < prev    next >
C/C++ Source or Header  |  1993-08-16  |  14KB  |  458 lines

  1. /*
  2.  * Copyright 1991, 1992 Eric R. Smith
  3.  * Copyright 1992 Atari Corporation.
  4.  * All rights reserved.
  5.  */
  6.  
  7. /* 
  8.  * The rendezvous: 
  9.  * a little bit of message passing with a lot of synchronization.
  10.  *
  11.  * Pmsg(mode,mboxid,msgptr);
  12.  * short mode;
  13.  * long mboxid;
  14.  * struct { long msg1, msg2; short pid; } *msgptr;
  15.  *
  16.  * 'mode' bits:
  17.  *   0000    read
  18.  *   0001    write
  19.  *   0002    write, then read from mboxid "PDxx" where xx is my pid.
  20.  *
  21.  *   8000    OR with this bit to make the operation non-blocking.
  22.  *
  23.  * The messages are five words long: two longs and a short, in that order. 
  24.  * The values  of the first two longs are totally up to the processes in
  25.  * question.  The value of the short is the PID of the sender.  On return
  26.  * from writes, the short is the PID of the process that read your message.
  27.  * On return from reads, it's the PID of the writer.
  28.  *
  29.  * If the 0x8000 bit is set in the mode, and there is not a reader/writer
  30.  * for the mboxid already on the WAIT_Q, this call returns -1.
  31.  *
  32.  * In mode 2, the writer is declaring that it wants to wait for a reply to
  33.  * the message.  What happens is that the reader gets put on the ready
  34.  * queue, but the writer is atomically turned into a reader on a mailbox
  35.  * whose mboxid is (0xFFFF0000 .OR. pid). The idea is that this process
  36.  * will sleep until awoken at a later time by the process that read the
  37.  * message.  The process reading the original request is guaranteed not to
  38.  * block when writing the reply.
  39.  *
  40.  * There is no provision for a timeout.
  41.  
  42. The flow is as follows:
  43.  
  44.     if (you're writing)
  45.     if (there's somebody waiting to read)
  46.                         (SATISFY THE RENDEZVOUS RIGHT AWAY)
  47.         copy msg from your data space into his proc structure
  48.         wake him up
  49.         if (you're doing a write/read)
  50.                     (TURN AROUND AND BLOCK WAITING TO READ)
  51.         write mode 0 to your proc struct and change your mbid
  52.         goto dosleep
  53.         else
  54.         fill in his PID in your data space
  55.         return 0;
  56.         endif
  57.     else
  58.                        (YOU HAVE TO BLOCK WAITING FOR A WRITER)
  59.         copy from your data space to your proc structure
  60.         goto dosleep
  61.     endif
  62.     else (you're reading)
  63.     if (there's somebody waiting to write)
  64.                         (SATISFY THE RENDEZVOUS RIGHT AWAY)
  65.         copy msg from his proc to your data space
  66.         if (he's doing a write/read)
  67.                      (TURN HIM AROUND WAITING FOR A WRITER)
  68.         change his mode from write/read to read, and his mbid
  69.         leave him asleep
  70.         else
  71.         wake him up
  72.         endif
  73.         return 0;
  74.     else
  75.                        (YOU HAVE TO BLOCK WAITING FOR A READER)
  76.         write mode 0 to your proc struct
  77.         goto dosleep
  78.     endif
  79.     endif
  80.  
  81. dosleep:
  82.     if (non-blocking mode) return EWOULDBLOCK;
  83.     do {
  84.         sleep(WAIT_Q, WAIT_MB);
  85.     } until (a rendezvous happens);
  86.     (when you wake up...)
  87.     if (you're reading)
  88.     copy from your proc struct to your data space
  89.     else
  90.     copy the writer's PID from your proc struct to your data space
  91.     endif
  92.     return 0;
  93.  
  94.  * The wait_cond we use is WAIT_MB, and the mbid waited for is another
  95.  * field in the proc structure, along with the two longs and a short you
  96.  * need to remember what somebody else wrote to you, and the mode field for
  97.  * what kind of operation you're doing (read, write, write/read).
  98.  *
  99.  * The beauty of this is that mailboxes don't need to be created or
  100.  * destroyed, and the blocking and turnaround are atomic, and it's a way to
  101.  * do rendezvous and interprocess communication without lots of system
  102.  * calls (Pgetpid, Fwrite, Pause, plus synchronization worries about having
  103.  * the reader get the message and Pkill you before you've Paused).
  104.  *
  105.  * Note: Say PID 33 writes in mode 2 to MBXX and it blocks. Then somebody
  106.  * writes to PD33, and blocks because you're not waiting for it.  Then your
  107.  * write is satisfied, so you become a reader on PD33.  We should check to
  108.  * see if there are any writers on PD33 and satisfy that rendezvous! But
  109.  * this could go on forever, and it's easier to stop here.  So you lose:
  110.  * this situation is timing sensitive.  It shouldn't come up anyway.
  111.  */
  112.  
  113. #include "mint.h"
  114.  
  115. long ARGS_ON_STACK
  116. p_msg(mode, mbid, ptr)
  117.     int mode;
  118.     long mbid;
  119.     char *ptr;
  120. {
  121.     int noblock;
  122.     PROC *p;
  123.  
  124.     TRACELOW(("Pmsg(%d,%lx,%lx)",mode,mbid,ptr));
  125.  
  126.     noblock = (mode & 0x8000);
  127.     mode &= ~0x8000;
  128.  
  129.     if (mode == 0) {
  130.     /* read */
  131.     /* walk the whole process list looking for a writer */
  132.     for (p = proclist; p; p = p->gl_next) {
  133.         if ((p->mb_mode == 1 || p->mb_mode == 2) && p->mb_mbid == mbid) {
  134.         /* this process is trying to write this mbox */
  135.         goto got_rendezvous;
  136.         }
  137.     }
  138.     /* nobody is writing just now */
  139.     goto dosleep;
  140.     }
  141.     else if (mode == 1 || mode == 2) {
  142.     /* write, or write/read */
  143.     /* walk the whole process list looking for a reader */
  144.     for (p = proclist; p; p = p->gl_next) {
  145.         if (p->mb_mode == 0 && p->mb_mbid == mbid) {
  146.         /* this process is reading this mbox */
  147.         goto got_rendezvous;
  148.         }
  149.     }
  150.     /* nobody is reading just now */
  151.     curproc->mb_long1 = *(long *)ptr;    /* copy the message */
  152.     curproc->mb_long2 = *(long *)(ptr+4);    /* into my proc struct */
  153.     goto dosleep;
  154.     }
  155.     else return EINVFN;            /* invalid mode */
  156.  
  157. /*
  158.  * we get here if we actually have a rendezvous between p and curproc.
  159.  */
  160.  
  161. got_rendezvous:
  162.     if (!mode) {
  163.     /* curproc is reading */
  164.     *(long *)(ptr) = p->mb_long1;        /* copy the message */
  165.     *(long *)(ptr+4) = p->mb_long2;        /* from his proc struct */
  166.     *(short *)(ptr+8) = p->pid;        /* provide the PID */
  167.  
  168.     if (p->mb_mode == 2) {
  169.         /*
  170.          * The blocked write was in mode 2: writer becomes a reader and
  171.          * stays on WAIT_Q waiting on WAIT_MB.
  172.          */
  173.         p->mb_mbid = 0xFFFF0000L | p->pid;
  174.         p->mb_mode = 0;
  175.     }
  176.     else {
  177.         /* The blocked write was in mode 1: writer wakes up */
  178.         p->mb_writer = curproc->pid;    /* tell writer reader's pid */
  179.         p->mb_mode = -1;            /* mark rendezvous */
  180.         p->wait_cond = 0;            /* not necessary? */
  181.         if (p->wait_q != READY_Q) {
  182.             short sr = spl7();
  183.         /* wake up the writer if it is sleeping */
  184.         rm_q(p->wait_q,p);
  185.         add_q(READY_Q,p);
  186.         spl(sr);
  187.         }
  188.     }
  189.     return 0;
  190.     }
  191.     else {
  192.     /* curproc is writing */
  193.     p->mb_writer = curproc->pid;        /* provide the PID */
  194.     p->mb_long1 = *(long *)(ptr);        /* copy the message */
  195.     p->mb_long2 = *(long *)(ptr+4);
  196.     p->mb_mode = -1;            /* mark rendezvous */
  197.     p->wait_cond = 0;            /* not necessary? */
  198.     if (p->wait_q != READY_Q) {
  199.         short sr = spl7();
  200.         /* wake up the reader if it is sleeping */
  201.         rm_q(p->wait_q,p);
  202.         add_q(READY_Q,p);
  203.         spl(sr);
  204.     }
  205.     if (mode == 2) {
  206.         /* now curproc becomes a reader */
  207.         mbid = 0xFFFF0000L | curproc->pid;
  208.         mode = 0;
  209.         goto dosleep;
  210.     }
  211.     else {
  212.         *(short *)(ptr+8) = p->pid;        /* tell reader writer's pid */
  213.         return 0;
  214.     }
  215.     }
  216.  
  217. /*
  218.  * we get here when a read or write should block because there's nobody
  219.  * already waiting at the other end.
  220.  */
  221.  
  222. dosleep: 
  223.     if (noblock) {
  224.         return -1L;
  225.     }
  226.     curproc->mb_mbid = mbid;    /* and ID waited for */
  227.     curproc->mb_mode = mode;    /* save mode */
  228.  
  229. /*
  230.  * OK: now we sleep until a rendezvous has occured. The loop is because we
  231.  * may be woken up to deal with signals before the rendezvous.  The thing
  232.  * that says the rendezvous actually happened is that mb_mode got changed
  233.  * to -1.
  234.  */
  235.     do {
  236.         sleep(WAIT_Q, WAIT_MB);            /* block */
  237.     } while (curproc->mb_mode != -1);
  238.  
  239.     /*
  240.      * When we wake up, we transfer the message from our proc struct
  241.      * into our message pointer space if we were reading, and do
  242.      * nothing if we were writing.  The write/read case is taken care
  243.      * of without waking this process up at all.
  244.      *
  245.      * Check curproc->mode because it may not be the mode we started
  246.      * with any more.
  247.      */
  248.     if (mode == 0) {
  249.         *(long *)(ptr) = curproc->mb_long1;
  250.         *(long *)(ptr+4) = curproc->mb_long2;
  251.     }
  252.     /* in any case, copy the 'writer' field because that's the PID */
  253.     /* of the other side of the rendezvous */
  254.     *(short *)(ptr+8) = curproc->mb_writer;
  255.     return 0;
  256. }
  257.  
  258. /*
  259.  * more mutex: this time a semaphore.
  260.  *
  261.  * long Psemaphore(short mode, long id, long timeout)
  262.  *
  263.  *    MODE    ACTION
  264.  *      0    Create and get a semaphore with the given ID.
  265.  *      1    Destroy.
  266.  *      2    Get (blocks until it's available or destroyed, or timeout).
  267.  *      3    Release.
  268.  *
  269.  * RETURNS
  270.  *
  271.  *    CODE    MEANING
  272.  *      0    OK.  Created/obtained/released/destroyed, depending on mode.
  273.  *    ERROR    You asked for a semaphore that you already own.
  274.  *    ERANGE    That semaphore doesn't exist (modes 1, 2, 3, 4),
  275.  *        or out of slots for new semaphores (0).
  276.  *    EACCDN    That semaphore exists already, so you can't create it (mode 0),
  277.  *        or the semaphore is busy (returned from mode 3 if you lose),
  278.  *        or you don't own it (modes 1 and 4).
  279.  *
  280.  * If you create a semaphore you will also own it, so you have to release
  281.  * it with mode 2 before anybody else can get it.  Semaphore ID's are magic
  282.  * numbers which the creator should make available to the users. You have
  283.  * to own a semaphore to destroy it.  If you block waiting for a semaphore,
  284.  * and then it gets destroyed, you get ERANGE.
  285.  *
  286.  * The timeout argument is ignored except in mode 2.  In that mode, 0 means
  287.  * "return immediately" and, as a special case, -1 means "forever."
  288.  * Since it is in fact an unsigned long number of milliseconds, -2 means
  289.  * 49 hours, but here -1 truly means forever.
  290.  *
  291.  */
  292.  
  293. #define NSEMAPHORES 10
  294.  
  295. typedef struct sema {
  296.     struct sema *next;
  297.     long id;
  298.     short owner;    /* -1 means "available" */
  299. } SEMA;
  300.  
  301. static SEMA *semalist;
  302.  
  303. static void unsemame P_((PROC *));
  304.  
  305. static void
  306. unsemame(p)
  307. PROC *p;
  308. {
  309.     /* take process P off the WAIT_Q and put it on the READY_Q */
  310.     TRACE(("semaphore sleep ends for pid %d",p->pid));
  311.     if (p->wait_q == WAIT_Q) {
  312.         short sr = spl7();
  313.         rm_q(WAIT_Q,p);
  314.         add_q(READY_Q,p);
  315.         spl(sr);
  316.     }
  317.     p->wait_cond = 0;
  318. }
  319.  
  320. long ARGS_ON_STACK
  321. p_semaphore(mode,id,timeout)
  322. int mode;
  323. long id;
  324. long timeout;
  325. {
  326.     SEMA *s, **q;
  327.     TIMEOUT *timeout_ptr = NULL;
  328.  
  329.     TRACELOW(("Psemaphore(%d,%lx)",mode,id));
  330.  
  331.     switch (mode) {
  332.     case 0: {            /* create */
  333.         for (s = semalist; s; s = s->next) {
  334.         if (s->id == id) {
  335.             DEBUG(("Psemaphore(%d,%lx): already exists",mode,id));
  336.             return EACCDN;
  337.         }
  338.         }
  339.         /* get a new one */
  340.         if ((s = (SEMA *)kmalloc(sizeof(SEMA))) == 0)
  341.         return ENSMEM;
  342.         s->id = id;
  343.         s->owner = curproc->pid;
  344.         s->next = semalist;
  345.         semalist = s;
  346.         return E_OK;
  347.     }
  348.     case 2: {            /* get */
  349. loop:
  350.         for (s = semalist; s; s = s->next) {
  351.         if (s->id == id) {
  352.             /* found your semaphore */
  353.             if (s->owner == curproc->pid) {
  354.                 DEBUG(("Psemaphore(%d,%lx): curproc already owns it!",
  355.                     mode,id));
  356.                 return ERROR;
  357.             }
  358.             if (s->owner == -1) {
  359.             /* it's free; you get it */
  360.             s->owner = curproc->pid;
  361.             if (timeout_ptr) canceltimeout(timeout_ptr);
  362.             return 0;
  363.             }
  364.             else {
  365.             /* not free */
  366.             if (timeout == 0) {
  367.                 /* non-blocking mode */
  368.                 return EACCDN;
  369.             }
  370.             else {
  371.                 if (timeout != -1 && !timeout_ptr) {
  372.                 /* schedule a timeout */
  373.                 timeout_ptr = addtimeout(timeout,unsemame);
  374.                 }
  375.                 /* block until it's released, then try again */
  376.                 sleep(WAIT_Q,WAIT_SEMA);
  377.                 if (curproc->wait_cond != WAIT_SEMA) {
  378.                 TRACE(("Psemaphore(%d,%lx) timed out",mode,id));
  379.                 return EACCDN;
  380.                 }
  381.                 goto loop;
  382.             }
  383.             }
  384.         }
  385.         }
  386.         /* no such semaphore (possibly deleted while we were waiting) */
  387.         if (timeout_ptr) canceltimeout(timeout_ptr);
  388.         DEBUG(("Psemaphore(%d,%lx): no such semaphore",mode,id));
  389.         return ERANGE;
  390.     }
  391.     case 3:                /* release */
  392.     case 1: {            /* destroy */
  393.         /*
  394.          * Style note: this is a handy way to chain down a linked list.
  395.          * q follows along behind s pointing at the "next" field of the
  396.          * previous list element.  To start, q points to the semalist
  397.          * variable itself.  At all times, then, *q is the place to
  398.          * write s->next when you want to remove an element 's' from
  399.          * the list.
  400.          */
  401.         for (s = *(q = &semalist); s; s = *(q = &s->next)) {
  402.         if (s->id == id) {
  403.             /* found your semaphore */
  404.             if (s->owner != curproc->pid) {
  405.             DEBUG(("Psemaphore(%d,%lx): you don't own it",mode,id));
  406.             return EACCDN;
  407.             }
  408.             else {
  409.             if (mode == 3) {
  410.                 s->owner = -1;    /* make it free, or */
  411.             } else {
  412.                 *q = s->next;        /* delete from list */
  413.                 kfree(s);        /* and free it */
  414.             }
  415.             /* wake up anybody who's waiting for a semaphore */
  416.             wake(WAIT_Q,WAIT_SEMA);
  417.             return E_OK;
  418.             }
  419.         }
  420.         }
  421.         /* no such semaphore */
  422.         DEBUG(("Psemaphore(%d,%lx): no such semaphore",mode,id));
  423.         return ERANGE;
  424.     }
  425.     default: {            /* invalid mode argument */
  426.         DEBUG(("Psemaphore(%d,%lx): invalid mode",mode,id));
  427.         return EINVFN;
  428.     }
  429.     }
  430. }
  431.  
  432. /*
  433.  * Release all semaphores owned by the process with process id "pid".
  434.  * The semaphores are only released, not destroyed. This function
  435.  * is called from terminate() in dos_mem.c during process termination.
  436.  */
  437.  
  438. void
  439. free_semaphores(pid)
  440.     int pid;
  441. {
  442.     SEMA *s;
  443.     int didsomething = 0;        /* did any semaphores get freed? */
  444.  
  445.     for (s = semalist; s; s = s->next) {
  446.         if (s->owner == pid) {
  447.             s->owner = -1;        /* mark the semaphore as free */
  448.             didsomething = 1;
  449.         }
  450.     }
  451.     if (didsomething) {
  452.     /* wake up anybody waiting for a semaphore, in case it's one of
  453.      * the ones we just freed
  454.      */
  455.         wake(WAIT_Q, WAIT_SEMA);
  456.     }
  457. }
  458.